package com.seekting.simplewebrtc;

import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;

import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.AudioSource;
import org.webrtc.AudioTrack;
import org.webrtc.Camera2Enumerator;
import org.webrtc.CameraEnumerator;
import org.webrtc.DataChannel;
import org.webrtc.EglBase;
import org.webrtc.IceCandidate;
import org.webrtc.Logging;
import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream;
import org.webrtc.PeerConnection;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.RendererCommon;
import org.webrtc.RtpReceiver;
import org.webrtc.RtpSender;
import org.webrtc.SdpObserver;
import org.webrtc.SessionDescription;
import org.webrtc.SurfaceViewRenderer;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoFileRenderer;
import org.webrtc.VideoRenderer;
import org.webrtc.VideoSource;
import org.webrtc.VideoTrack;
import org.webrtc.voiceengine.WebRtcAudioManager;
import org.webrtc.voiceengine.WebRtcAudioRecord;
import org.webrtc.voiceengine.WebRtcAudioTrack;
import org.webrtc.voiceengine.WebRtcAudioUtils;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import de.tavendo.autobahn.WebSocket;
import de.tavendo.autobahn.WebSocketConnection;
import de.tavendo.autobahn.WebSocketException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okio.BufferedSink;

import static com.seekting.simplewebrtc.Utils.preferCodec;

public class MainActivity extends AppCompatActivity {
    public static final String URL = "http://35.160.246.46:8080";
    private static final String ROOM_JOIN = "join";
    private static final String ROOM_MESSAGE = "message";
    private static final String ROOM_LEAVE = "leave";
    public static final String VIDEO_TRACK_ID = "ARDAMSv0";
    public static final String AUDIO_TRACK_ID = "ARDAMSa0";
    public static final String VIDEO_TRACK_TYPE = "video";
    public static final boolean DEBUG = AppEnv.bAppdebug;
    public static final String TAG = "MainActivity";
    public static final int VIDEO_WIDTH = 1280;
    public static final int VIDEO_HEIGHT = 720;
    public static final int videoFps = 15;
    private EglBase rootEglBase;
    private SurfaceViewRenderer pipRenderer;
    private SurfaceViewRenderer fullscreenRenderer;
    private VideoFileRenderer videoFileRenderer;
    private boolean isSwappedFeeds = false;
    private RoomConnectionParameters connectionParameters;
    public static String roomId = "3333345";
    public static String ROOM_URL = URL + "/" + ROOM_JOIN + "/" + roomId;
    private MediaConstraints pcConstraints;
    MediaConstraints audioConstraints;
    MediaConstraints sdpMediaConstraints;
    private LinkedList queuedRemoteCandidates;
    private PeerConnectionFactory factory;
    private String preferredVideoCodec;
    private PeerConnection peerConnection;
    private DataChannel dataChannel;
    private MediaStream mediaStream;
    private VideoCapturer capturer;
    private VideoSource videoSource;
    private VideoTrack localVideoTrack;
    private final ProxyRenderer remoteProxyRenderer = new ProxyRenderer("remote");
    private final ProxyRenderer localProxyRenderer = new ProxyRenderer("local");

    private final List<VideoRenderer.Callbacks> remoteRenderers =
            new ArrayList<VideoRenderer.Callbacks>();
    private boolean initiator = false;
    private String messageUrl = "";
    private String leaveUrl = "";


    private ExecutorService executor = Executors.newSingleThreadExecutor();
    private AudioSource audioSource;
    private AudioTrack localAudioTrack;
    private RtpSender localVideoSender;
    private SDPObserver sdpObserver;
    private WebSocketConnection mWebSocketConnection;
    private Handler bgHandler;
    private HandlerThread mHandlerThread;

    private SessionDescription mLocaleSDP;
    private String clientId;
    private WebSocketObserver mWebSocketObserver;


    private class ProxyRenderer implements VideoRenderer.Callbacks {
        private VideoRenderer.Callbacks target;
        String name;

        public ProxyRenderer(String name) {
            this.name = name;
        }

        synchronized public void renderFrame(VideoRenderer.I420Frame frame) {
            if (target == null) {
                Logging.d(TAG, "Dropping frame in proxy because target is null.");
                VideoRenderer.renderFrameDone(frame);
                return;
            }

            target.renderFrame(frame);
        }

        synchronized public void setTarget(VideoRenderer.Callbacks target) {
            this.target = target;
        }
    }


    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        pipRenderer = (SurfaceViewRenderer) findViewById(R.id.pip_video_view);
        pipRenderer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                setSwappedFeeds(!isSwappedFeeds);
            }
        });
        fullscreenRenderer = (SurfaceViewRenderer) findViewById(R.id.fullscreen_video_view);
        mHandlerThread = new HandlerThread("websocket");
        mHandlerThread.start();
        bgHandler = new Handler(mHandlerThread.getLooper());

        // Create video renderers.
        rootEglBase = EglBase.create();
        pipRenderer.init(rootEglBase.getEglBaseContext(), null);
        pipRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);


        fullscreenRenderer.init(rootEglBase.getEglBaseContext(), null);
        fullscreenRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);

        pipRenderer.setZOrderMediaOverlay(true);
        pipRenderer.setEnableHardwareScaler(true /* enabled */);
        fullscreenRenderer.setEnableHardwareScaler(true /* enabled */);
        remoteRenderers.add(remoteProxyRenderer);
        executor.execute(new Runnable() {
            @Override
            public void run() {

                createPeerConnectionFactory();
            }
        });

        setSwappedFeeds(true /* isSwappedFeeds */);

        WebRtcFetcher webRtcFetcher = new WebRtcFetcher();

        if (DEBUG) {
            Log.d(TAG, "onCreate.");
        }

        connectionParameters = new RoomConnectionParameters(URL, roomId, false, null);
        webRtcFetcher.request(new WebRtcFetcher.OnFetchCallBack() {
            @Override
            public void onSignalingParametersReady(final SignalingParameters signalingParameters) {
                if (DEBUG) {
                    Log.d(TAG, "onSignalingParametersReady." + signalingParameters);
                }
                initiator = signalingParameters.initiator;
                messageUrl = getMessageUrl(connectionParameters, signalingParameters);
                leaveUrl = getLeaveUrl(connectionParameters, signalingParameters);
                clientId = signalingParameters.clientId;
                Log.d("seekting", "initiator:" + initiator);
                Log.d("seekting", "messageUrl:" + messageUrl);
                Log.d("seekting", "leaveUrl:" + leaveUrl);

                executor.execute(new Runnable() {
                    @Override
                    public void run() {

                        createPeerConnection(signalingParameters);
                    }
                });


            }
        });


    }

    private void createPeerConnectionFactory() {
        PeerConnectionFactory.initializeInternalTracer();
        preferredVideoCodec = "H264";
        PeerConnectionFactory.initializeFieldTrials("WebRTC-IntelVP8/Enabled/");
        WebRtcAudioManager.setBlacklistDeviceForOpenSLESUsage(true /* enable */);
        WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true);
        WebRtcAudioUtils.setWebRtcBasedAutomaticGainControl(false);
        WebRtcAudioUtils.setWebRtcBasedNoiseSuppressor(false);
        WebRtcAudioRecord.setErrorCallback(new WebRtcAudioRecord.WebRtcAudioRecordErrorCallback() {
            @Override
            public void onWebRtcAudioRecordInitError(String errorMessage) {
                Log.e(TAG, "onWebRtcAudioRecordInitError: " + errorMessage);
                reportError(errorMessage);
            }

            @Override
            public void onWebRtcAudioRecordStartError(
                    WebRtcAudioRecord.AudioRecordStartErrorCode errorCode, String errorMessage) {
                Log.e(TAG, "onWebRtcAudioRecordStartError: " + errorCode + ". " + errorMessage);
                reportError(errorMessage);
            }

            @Override
            public void onWebRtcAudioRecordError(String errorMessage) {
                Log.e(TAG, "onWebRtcAudioRecordError: " + errorMessage);
                reportError(errorMessage);
            }
        });

        WebRtcAudioTrack.setErrorCallback(new WebRtcAudioTrack.WebRtcAudioTrackErrorCallback() {
            @Override
            public void onWebRtcAudioTrackInitError(String errorMessage) {
                reportError(errorMessage);
            }

            @Override
            public void onWebRtcAudioTrackStartError(String errorMessage) {
                reportError(errorMessage);
            }

            @Override
            public void onWebRtcAudioTrackError(String errorMessage) {
                reportError(errorMessage);
            }
        });
        PeerConnectionFactory.initializeAndroidGlobals(
                this, true);
        factory = new PeerConnectionFactory(null);

    }

    private void reportError(String errorMessage) {
        Log.e("seekting", "MainActivity.reportError()" + errorMessage);
    }


    private void createPeerConnection(final SignalingParameters signalingParameters) {
        pcConstraints = new MediaConstraints();
        pcConstraints.optional.add(
                new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));

        audioConstraints = new MediaConstraints();

        sdpMediaConstraints = new MediaConstraints();
        sdpMediaConstraints.mandatory.add(
                new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
        sdpMediaConstraints.mandatory.add(
                new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));


        queuedRemoteCandidates = new LinkedList<IceCandidate>();
        factory.setVideoHwAccelerationOptions(rootEglBase.getEglBaseContext(), rootEglBase.getEglBaseContext());
        PeerConnection.RTCConfiguration rtcConfig =
                new PeerConnection.RTCConfiguration(signalingParameters.iceServers);
        // TCP candidates are only useful when connecting to a server that supports
        // ICE-TCP.
        rtcConfig.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.DISABLED;
        rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE;
        rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE;
        rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
        // Use ECDSA encryption.
        rtcConfig.keyType = PeerConnection.KeyType.ECDSA;
        peerConnection = factory.createPeerConnection(rtcConfig, pcConstraints, new PCObserver());

        DataChannel.Init init = new DataChannel.Init();
        init.ordered = true;
        init.negotiated = false;
        init.maxRetransmits = -1;
        init.maxRetransmitTimeMs = -1;
        init.id = -1;
        init.protocol = "";
        dataChannel = peerConnection.createDataChannel("ApprtcDemo data", init);
        mediaStream = factory.createLocalMediaStream("ARDAMS");
        capturer = createVideoCapturer();

        videoSource = factory.createVideoSource(capturer);

        capturer.startCapture(VIDEO_WIDTH, VIDEO_HEIGHT, videoFps);

        localVideoTrack = factory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
        localVideoTrack.setEnabled(true);
        localVideoTrack.addRenderer(new VideoRenderer(localProxyRenderer));
        mediaStream.addTrack(localVideoTrack);

        audioSource = factory.createAudioSource(audioConstraints);
        localAudioTrack = factory.createAudioTrack(AUDIO_TRACK_ID, audioSource);
        localAudioTrack.setEnabled(true);
        mediaStream.addTrack(localAudioTrack);
        peerConnection.addStream(mediaStream);
        findVideoSender();
        if (signalingParameters.initiator) {
            sdpObserver = new SDPObserver();
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    //这里会注册广播
                    peerConnection.createOffer(sdpObserver, sdpMediaConstraints);
                }
            });
        }

        bgHandler.post(new Runnable() {
            @Override
            public void run() {
                mWebSocketConnection = new WebSocketConnection();
                mWebSocketObserver = new WebSocketObserver();
                try {
                    mWebSocketConnection.connect(new URI(signalingParameters.wssUrl), mWebSocketObserver);
                } catch (WebSocketException e) {
                    e.printStackTrace();
                } catch (URISyntaxException e) {
                    e.printStackTrace();
                }

                //此处不需要注册
//                JSONObject json = new JSONObject();
//                try {
//                    json.put("cmd", "register");
//                    json.put("roomid", connectionParameters.roomId);
//                    json.put("clientid", signalingParameters.clientId);
//                    Log.d(TAG, "C->WSS: " + json.toString());
//                    mWebSocketConnection.sendTextMessage(json.toString());
//                } catch (JSONException e) {
//                    e.printStackTrace();
//                }
            }
        });


    }

    private void findVideoSender() {
        for (RtpSender sender : peerConnection.getSenders()) {
            if (sender.track() != null) {
                String trackType = sender.track().kind();
                if (trackType.equals(VIDEO_TRACK_TYPE)) {
                    Log.d(TAG, "Found video sender.");
                    localVideoSender = sender;
                }
            }
        }
    }


    private VideoCapturer createVideoCapturer() {

        VideoCapturer videoCapturer = createCameraCapturer(new Camera2Enumerator(this));

        return videoCapturer;
    }

    private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) {
        final String[] deviceNames = enumerator.getDeviceNames();

        // First, try to find front facing camera
        Logging.d(TAG, "Looking for front facing cameras.");
        for (String deviceName : deviceNames) {
            if (enumerator.isFrontFacing(deviceName)) {
                Logging.d(TAG, "Creating front facing camera capturer.");
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);

                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }

        // Front facing camera not found, try something else
        Logging.d(TAG, "Looking for other cameras.");
        for (String deviceName : deviceNames) {
            if (!enumerator.isFrontFacing(deviceName)) {
                Logging.d(TAG, "Creating other camera capturer.");
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);

                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }

        return null;
    }

    /**
     * 连上为false pip是小的
     *
     * @param isSwappedFeeds
     */
    private void setSwappedFeeds(boolean isSwappedFeeds) {
        Logging.d(TAG, "setSwappedFeeds: " + isSwappedFeeds);
        Log.d("seekting", "MainActivity.setSwappedFeeds()" + isSwappedFeeds);
        this.isSwappedFeeds = isSwappedFeeds;

        localProxyRenderer.setTarget(isSwappedFeeds ? fullscreenRenderer : pipRenderer);
        remoteProxyRenderer.setTarget(isSwappedFeeds ? pipRenderer : fullscreenRenderer);
        fullscreenRenderer.setMirror(isSwappedFeeds);
        pipRenderer.setMirror(!isSwappedFeeds);
    }

    private String getMessageUrl(
            RoomConnectionParameters connectionParameters, SignalingParameters signalingParameters) {
        return connectionParameters.roomUrl + "/" + ROOM_MESSAGE + "/" + connectionParameters.roomId
                + "/" + signalingParameters.clientId + getQueryString(connectionParameters);
    }

    private String getLeaveUrl(
            RoomConnectionParameters connectionParameters, SignalingParameters signalingParameters) {
        return connectionParameters.roomUrl + "/" + ROOM_LEAVE + "/" + connectionParameters.roomId + "/"
                + signalingParameters.clientId + getQueryString(connectionParameters);
    }

    private String getQueryString(RoomConnectionParameters connectionParameters) {
        if (connectionParameters.urlParameters != null) {
            return "?" + connectionParameters.urlParameters;
        } else {
            return "";
        }
    }

    private void closeInternal() {
        remoteProxyRenderer.setTarget(null);
        localProxyRenderer.setTarget(null);
        if (mWebSocketConnection != null) {
            bgHandler.post(new Runnable() {
                @Override
                public void run() {
                    mWebSocketConnection.disconnect();
                    mWebSocketConnection = null;
                    OkHttpClient.Builder bu = new OkHttpClient.Builder();
                    OkHttpClient k = bu.build();
                    Call c = k.newCall(new Request.Builder().url(leaveUrl).post(new RequestBody() {
                        @Override
                        public MediaType contentType() {

                            MediaType media = MediaType.parse("text/plain; charset=utf-8");
                            if (DEBUG) {
                                Log.d(TAG, "contentType.");
                            }
                            return media;
                        }

                        @Override
                        public void writeTo(BufferedSink sink) throws IOException {

                        }
                    }).build());
                    c.enqueue(new Callback() {
                        @Override
                        public void onFailure(Call call, IOException e) {

                        }

                        @Override
                        public void onResponse(Call call, Response response) throws IOException {
                            Log.d("seekting", "onLeave." + response.body().string());
                        }
                    });
                    mWebSocketObserver.waitClose();
                    bgHandler.getLooper().quit();
                }
            });
        }
        if (dataChannel != null) {
            dataChannel.dispose();
            dataChannel = null;
        }
        if (peerConnection != null) {
            peerConnection.dispose();
            Log.d("seekting", "peerConnection.dispose()");
            peerConnection = null;
        }
        executor.execute(new Runnable() {
            @Override
            public void run() {


                Log.d(TAG, "Closing audio source.");
                if (audioSource != null) {
                    audioSource.dispose();
                    audioSource = null;
                }
                Log.d(TAG, "Stopping capture.");
                if (capturer != null) {
                    try {
                        capturer.stopCapture();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    capturer.dispose();
                    capturer = null;
                }
                Log.d(TAG, "Closing video source.");
                if (videoSource != null) {
                    videoSource.dispose();
                    videoSource = null;
                }
                Log.d(TAG, "Closing peer connection factory.");
                if (factory != null) {
                    factory.dispose();
                    factory = null;
                    PeerConnectionFactory.stopInternalTracingCapture();
                    PeerConnectionFactory.shutdownInternalTracer();
                }
            }
        });
        if (pipRenderer != null) {
            pipRenderer.release();
            pipRenderer = null;
        }
        if (fullscreenRenderer != null) {
            fullscreenRenderer.release();
            fullscreenRenderer = null;
        }
        if (rootEglBase != null) {
            rootEglBase.release();
            rootEglBase = null;
        }

    }

    @Override
    protected void onDestroy() {
        
        disconnect();
        super.onDestroy();

    }

    private void disconnect() {

        Log.d("seekting","MainActivity.disconnect()");

        closeInternal();

    }

    private class PCObserver implements PeerConnection.Observer {
        @Override
        public void onIceCandidate(final IceCandidate candidate) {
            Log.d("seekting", "PCObserver.onIceCandidate()" + candidate);
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    JSONObject json = new JSONObject();
                    jsonPut(json, "type", "candidate");
                    jsonPut(json, "label", candidate.sdpMLineIndex);
                    jsonPut(json, "id", candidate.sdpMid);
                    jsonPut(json, "candidate", candidate.sdp);
                    new AsyncPostTask(messageUrl, json.toString(), new AsyncPostTask.CallBack() {
                        @Override
                        public void onSuc(String str) {
                            Log.d("seekting", "onIceCandidate.发送成功！" + str);

                        }

                        @Override
                        public void onError() {

                        }
                    }).send();
                }
            });
        }

        @Override
        public void onIceCandidatesRemoved(final IceCandidate[] candidates) {
            Log.d("seekting", "PCObserver.onIceCandidatesRemoved()");
            executor.execute(new Runnable() {
                @Override
                public void run() {
//                    events.onIceCandidatesRemoved(candidates);
                }
            });
        }

        @Override
        public void onSignalingChange(PeerConnection.SignalingState newState) {
            Log.d(TAG, "SignalingState: " + newState);
        }

        @Override
        public void onIceConnectionChange(final PeerConnection.IceConnectionState newState) {
            Log.d("seekting", "PCObserver.onIceConnectionChange()" + newState);
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    Log.d(TAG, "IceConnectionState: " + newState);
                    Log.d("seekting", "onIceConnectionChange" + newState);
                    if (newState == PeerConnection.IceConnectionState.CONNECTED) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {

                                setSwappedFeeds(false /* isSwappedFeeds */);
                            }
                        });
                    } else if (newState == PeerConnection.IceConnectionState.DISCONNECTED) {
                        disconnect();
                        finish();
                    }
// else if (newState == PeerConnection.IceConnectionState.FAILED) {
//                        reportError("ICE connection failed.");
//                    }
                }
            });
        }

        @Override
        public void onIceGatheringChange(PeerConnection.IceGatheringState newState) {
            Log.d(TAG, "IceGatheringState: " + newState);
            Log.d("seekting", "PCObserver.onIceGatheringChange()" + newState);
        }

        @Override
        public void onIceConnectionReceivingChange(boolean receiving) {
            Log.d(TAG, "IceConnectionReceiving changed to " + receiving);
            Log.d("seekting", "PCObserver.onIceConnectionReceivingChange()" + receiving);
        }

        @Override
        public void onAddStream(final MediaStream stream) {
            Log.d("seekting", "PCObserver.onAddStream()");

            executor.execute(new Runnable() {
                @Override
                public void run() {
                    if (peerConnection == null  /*||isError*/) {
                        return;
                    }
                    if (stream.audioTracks.size() > 1 || stream.videoTracks.size() > 1) {
                        reportError("Weird-looking stream: " + stream);
                        return;
                    }
                    if (stream.videoTracks.size() == 1) {
                        VideoTrack remoteVideoTrack = stream.videoTracks.get(0);
                        remoteVideoTrack.setEnabled(true);
                        for (VideoRenderer.Callbacks remoteRender : remoteRenderers) {
                            remoteVideoTrack.addRenderer(new VideoRenderer(remoteRender));
                        }
                    }
                }
            });
        }

        @Override
        public void onRemoveStream(final MediaStream stream) {
            Log.d("seekting", "PCObserver.onRemoveStream()");
            executor.execute(new Runnable() {
                @Override
                public void run() {
//                    remoteVideoTrack = null;
                }
            });
        }

        @Override
        public void onDataChannel(final DataChannel dc) {
            Log.d("seekting", "PCObserver.onDataChannel()");
            Log.d(TAG, "New Data channel " + dc.label());

//            if (!dataChannelEnabled)
//                return;

            dc.registerObserver(new DataChannel.Observer() {
                public void onBufferedAmountChange(long previousAmount) {
                    Log.d(TAG, "Data channel buffered amount changed: " + dc.label() + ": " + dc.state());
                }

                @Override
                public void onStateChange() {
                    Log.d(TAG, "Data channel state changed: " + dc.label() + ": " + dc.state());
                }

                @Override
                public void onMessage(final DataChannel.Buffer buffer) {
                    if (buffer.binary) {
                        Log.d(TAG, "Received binary msg over " + dc);
                        return;
                    }
                    ByteBuffer data = buffer.data;
                    final byte[] bytes = new byte[data.capacity()];
                    data.get(bytes);
                    String strData = new String(bytes);
                    Log.d(TAG, "Got msg: " + strData + " over " + dc);
                }
            });
        }

        @Override
        public void onRenegotiationNeeded() {
            Log.d("seekting", "PCObserver.onRenegotiationNeeded()");
            // No need to do anything; AppRTC follows a pre-agreed-upon
            // signaling/negotiation protocol.
        }

        @Override
        public void onAddTrack(final RtpReceiver receiver, final MediaStream[] mediaStreams) {
            Log.d("seekting", "PCObserver.onAddTrack()");
        }
    }

    private class SDPObserver implements SdpObserver {


        @Override
        public void onCreateSuccess(final SessionDescription origSdp) {
            String sdpDescription = origSdp.description;
//            Log.d("seekting", "SDPObserver.onCreateSuccess()before" + sdpDescription);
            sdpDescription = preferCodec(sdpDescription, preferredVideoCodec, false);
//            Log.d("seekting", "SDPObserver.onCreateSuccess()end" + sdpDescription);
            final SessionDescription sdp = new SessionDescription(origSdp.type, sdpDescription);
            mLocaleSDP = sdp;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    Log.d("seekting", "Set local SDP from " + sdp.type);
                    peerConnection.setLocalDescription(sdpObserver, sdp);
                }
            });
        }

        @Override
        public void onSetSuccess() {
            Log.d("seekting", "SDPObserver.onSetSuccess()");
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    if (peerConnection == null) {
                        return;
                    }
                    if (initiator) {
                        // For offering peer connection we first create offer and set
                        // local SDP, then after receiving answer set remote SDP.
                        if (peerConnection.getRemoteDescription() == null) {
                            // We've just set our local SDP so time to send it.
                            Log.d(TAG, "Local SDP set succesfully");
                            JSONObject json = new JSONObject();
                            jsonPut(json, "sdp", mLocaleSDP.description);
                            jsonPut(json, "type", "offer");
                            AsyncPostTask asyncPostTask = new AsyncPostTask(messageUrl, json.toString(), new AsyncPostTask.CallBack() {
                                @Override
                                public void onSuc(String str) {
                                    Log.d("seekting", "sdp发送成功" + str);
                                }

                                @Override
                                public void onError() {

                                }
                            });
                            asyncPostTask.send();
                        } else {
                            // We've just set remote description, so drain remote
                            // and send local ICE candidates.
                            Log.d(TAG, "Remote SDP set succesfully");
//                            drainCandidates();
                        }
                    }
                    //else {
//                        // For answering peer connection we set remote SDP and then
//                        // create answer and set local SDP.
//                        if (peerConnection.getLocalDescription() != null) {
//                            // We've just set our local SDP so time to send it, drain
//                            // remote and send local ICE candidates.
//                            Log.d(TAG, "Local SDP set succesfully");
//                            events.onLocalDescription(localSdp);
//                            drainCandidates();
//                        } else {
//                            // We've just set remote SDP - do nothing for now -
//                            // answer will be created soon.
//                            Log.d(TAG, "Remote SDP set succesfully");
//                        }
//                    }
                }
            });
        }

        @Override
        public void onCreateFailure(String error) {
            Log.d("seekting", "SDPObserver.onCreateFailure()");

        }

        @Override
        public void onSetFailure(String error) {
            Log.d("seekting", "SDPObserver.onSetFailure()");

        }
    }

    class WebSocketObserver implements WebSocket.WebSocketConnectionObserver {
        boolean isClose = false;
        Object lock = new Object();

        @Override
        public void onOpen() {
            Log.d("seekting", "WebSocketObserver.onOpen()");
            JSONObject json = new JSONObject();
            try {
                json.put("cmd", "register");
                json.put("roomid", connectionParameters.roomId);
                json.put("clientid", clientId);
                Log.d(TAG, "C->WSS: " + json.toString());
                mWebSocketConnection.sendTextMessage(json.toString());
            } catch (JSONException e) {
                e.printStackTrace();
            }

        }

        public void waitClose() {
            synchronized (lock) {
                while (!isClose) {
                    try {
                        lock.wait(1000);
                        Log.d("seekting", "WebSocketObserver.waitClose()");
                        break;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        @Override
        public void onClose(WebSocketCloseNotification webSocketCloseNotification, String s) {

            Log.d("seekting", "WebSocketObserver.onClose()");
            synchronized (lock) {
                isClose = true;
                lock.notifyAll();
            }
        }

        @Override
        public void onTextMessage(String msg) {
            Log.d("seekting", "WebSocketObserver.onTextMessage()" + msg);
            try {
                JSONObject json = new JSONObject(msg);
                String msgText = json.getString("msg");
                String errorText = json.optString("error");
                if (msgText.length() > 0) {
                    json = new JSONObject(msgText);
                    String type = json.optString("type");
                    if (type.equals("candidate")) {
                        final IceCandidate candidate = Utils.toJavaCandidate(json);
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                peerConnection.addIceCandidate(candidate);
                            }
                        });


                    } else if (type.equals("answer")) {
                        if (initiator) {
                            SessionDescription sdp = new SessionDescription(
                                    SessionDescription.Type.fromCanonicalForm(type), json.getString("sdp"));
                            String sdpDescription = sdp.description;
                            sdpDescription = preferCodec(sdpDescription, preferredVideoCodec, false);
                            Log.d(TAG, "Set remote SDP.");
                            Log.d("seekting", "Set remote SDP type" + sdp.type);
                            Log.d("seekting", "Set remote SDP" + sdpDescription);
                            SessionDescription sdpRemote = new SessionDescription(sdp.type, sdpDescription);
                            peerConnection.setRemoteDescription(sdpObserver, sdpRemote);
                        } else {
                            reportError("Received answer for call initiator: " + msg);
                        }
                    } else if (type.equals("bye")) {
                        disconnect();
                        finish();

                    }
                }
            } catch (JSONException e) {
                e.printStackTrace();
            }

        }

        @Override
        public void onRawTextMessage(byte[] bytes) {

            Log.d("seekting", "WebSocketObserver.onRawTextMessage()");
        }

        @Override
        public void onBinaryMessage(byte[] bytes) {

            Log.d("seekting", "WebSocketObserver.onBinaryMessage()");
        }
    }

    private static void jsonPut(JSONObject json, String key, Object value) {
        try {
            json.put(key, value);
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
    }
}
